package com.fengwk.support.util;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.SystemPropertyUtils;

import com.fengwk.support.exception.BaseException;
import com.fengwk.support.util.LoggerUtils.Logger;

/**
 * 反射工具类
 */
public class ReflectUtils {

	private static final Logger LOG = LoggerUtils.getLogger(ReflectUtils.class);
	// 扫描 scanPackages 下的文件匹配符
	public static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
	
	private ReflectUtils() {
		throw new BaseException("can not instance " + getClass());
	}
	
	/**
	 * 检查类是否存在
	 * 
	 * @param className
	 * @return
	 */
	public static boolean hasClass(String className) {
		try {
			Class.forName(className);
		} catch (ClassNotFoundException e) {
			return false;
		}
		return true;
	}
	
	/**
	 * 注解扫描
	 * 
	 * @param src 源目标字节码
	 * @param annotation 注解字节码
	 * @return
	 */
	public static boolean hasAnnotation(Class<?> src, Class<? extends Annotation> annotation) {
		return src.getAnnotation(annotation) == null ? false : true;
	}
	
	public static Set<Class<?>> scanPackageClassByAnnotation(Collection<String> scanPackages, Class<? extends Annotation> annotation) {
		Set<Class<?>> annotationFullClazzSet = new HashSet<Class<?>>();
		Set<Class<?>> fullClazzSet = scanPackageClass(scanPackages);
		if (CollectionUtils.isNotBlank(fullClazzSet)) {
			for (Class<?> clazz: fullClazzSet) {
				if (hasAnnotation(clazz, annotation)) {
					annotationFullClazzSet.add(clazz);
				}
			}
		}
		return annotationFullClazzSet;
	}
	
	/**
	 * 找到scanPackages下的全部类
	 * 
	 * @param scanPackages
	 * @return
	 */
	public static Set<Class<?>> scanPackageClass(Collection<String> scanPackages) {
		Set<Class<?>> fullClazzSet = new HashSet<Class<?>>();
		if (CollectionUtils.isNotBlank(scanPackages)) {
			// 排重包路径，避免父子路径重复扫描
			Set<String> packages = checkPackages(scanPackages);
			// 获取Spring资源解析器
			ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
			// 创建Spring中用来读取resource为class的工具类
			MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
			for (String basePackage : packages) {
				if (StringUtils.isEmpty(basePackage)) {
					continue;
				}
				String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
						+ ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage))
						+ "/" + DEFAULT_RESOURCE_PATTERN;
				try {
					// 获取packageSearchPath下的Resource，这里得到的Resource是Class信息
					Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
					for (Resource resource : resources) {
						// 检查resource，这里的resource都是class
						String fullClassName = loadClassName(metadataReaderFactory, resource);
						Class<?> clazz = forName(fullClassName);
						if (clazz != null) {
							fullClazzSet.add(clazz);
						}
					}
				} catch (Exception e) {
				    LOG.error("获取包下面的类信息失败,package:" + basePackage, e);
				}
			}
		}
		return fullClazzSet;
	}

	/**
	 * 排重、检测package父子关系,避免多次扫描
	 * 
	 * @param scanPackages
	 * @return
	 */
	private static Set<String> checkPackages(Collection<String> scanPackages) {
		Set<String> packages = new HashSet<String>();
		if (CollectionUtils.isBlank(scanPackages)) {
			return packages;
		}
		// 排重路径
		packages.addAll(scanPackages);
		for (String packageStr : packages.toArray(new String[packages.size()])) {
			if (StringUtils.isEmpty(packageStr) || packageStr.equals(".") || packageStr.startsWith(".")) {
				continue;
			}
			if (packageStr.endsWith(".")) {
				packageStr = packageStr.substring(0, packageStr.length() - 1);
			}
			Iterator<String> packageIte = packages.iterator();
			boolean needAdd = true;
			while (packageIte.hasNext()) {
				String pack = packageIte.next();
				if (packageStr.startsWith(pack + ".")) {
					// 如果待加入的路径是已经加入的pack的子集，不加入
					needAdd = false;
				} else if (pack.startsWith(packageStr + ".")) {
					// 如果待加入的路径是已经加入的pack的父集，删除已加入的pack
					packageIte.remove();
				}
			}
			if (needAdd) {
				packages.add(packageStr);
			}
		}
		return packages;
	}

	/**
	 * 加载资源，根据resource获取className
	 * 
	 * @param metadataReaderFactory spring中用来读取resource为class的工具
	 * @param resource 这里的资源就是一个Class
	 */
	private static String loadClassName(MetadataReaderFactory metadataReaderFactory, Resource resource)
			throws IOException {
		try {
			if (resource.isReadable()) {
				MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
				if (metadataReader != null) {
					return metadataReader.getClassMetadata().getClassName();
				}
			}
		} catch (Exception e) {
		    LOG.error("根据Spring resource获取类名称失败", e);
		}
		return null;
	}

	/**
	 * 通过类全名获取class
	 * 
	 * @param fullClassName
	 * @param annotationClass
	 * @return
	 */
	public static Class<?> forName(String fullClassName) {
		// 利用反射，根据类名获取类的全部信息
		Class<?> clazz = null;
		try {
			clazz = Class.forName(fullClassName);
		} catch (Throwable e) {
			return null;
		}
		return clazz;
	}
	
	public static <T> T newProxy(Class<T> interfaceClass, InvocationHandler handler) {
	    Object object = Proxy.newProxyInstance(
	    		interfaceClass.getClassLoader(),
	    		new Class<?>[] { interfaceClass },
	    		handler);
	    return interfaceClass.cast(object);
	}
	
	/**
	 * 读取静态字段
	 * 
	 * @param path 包路径 + 空格 + 字段
	 * @return
	 */
	public static Object readStaticField(String path) {
		Object res = null;
		String[] paths = path.split(" ");
		if (paths != null && paths.length == 2) {
			String clazz = paths[0];
			String field = paths[1];
			try {
				Field f = Class.forName(clazz).getDeclaredField(field);
				f.setAccessible(true);
				if (!Modifier.isStatic(f.getModifiers())) {
					throw new BaseException("字段不是静态的");
				}
				try {
					res = f.get(null);
				} catch (IllegalArgumentException | IllegalAccessException e) {
					throw new BaseException(e.getMessage());
				}
			} catch (NoSuchFieldException | SecurityException | ClassNotFoundException e) {
				throw new BaseException(e.getMessage());
			}
		}
		return res;
	}
	
}
